package com.hanqian.kepler.web.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.hanqian.kepler.common.bean.NameValueVo;
import com.hanqian.kepler.common.bean.jqgrid.JqGridContent;
import com.hanqian.kepler.common.bean.jqgrid.JqGridReturn;
import com.hanqian.kepler.common.bean.result.AjaxResult;
import com.hanqian.kepler.common.enums.BaseEnumManager;
import com.hanqian.kepler.common.enums.DictEnum;
import com.hanqian.kepler.common.jpa.specification.Rule;
import com.hanqian.kepler.common.jpa.specification.SpecificationFactory;
import com.hanqian.kepler.core.entity.primary.core.Target;
import com.hanqian.kepler.core.entity.primary.core.TargetDesc;
import com.hanqian.kepler.core.entity.primary.sys.Dict;
import com.hanqian.kepler.core.service.core.TargetDescEvaluateService;
import com.hanqian.kepler.core.service.core.TargetDescService;
import com.hanqian.kepler.core.service.core.TargetDescZanService;
import com.hanqian.kepler.core.service.core.TargetService;
import com.hanqian.kepler.core.vo.WikiDescVo;
import com.hanqian.kepler.core.vo.WikiTreeVo;
import com.hanqian.kepler.flow.entity.User;
import com.hanqian.kepler.flow.enums.FlowEnum;
import com.hanqian.kepler.security.annotation.CurrentUser;
import it.uniroma1.dis.wsngroup.gexf4j.core.*;
import it.uniroma1.dis.wsngroup.gexf4j.core.data.Attribute;
import it.uniroma1.dis.wsngroup.gexf4j.core.data.AttributeClass;
import it.uniroma1.dis.wsngroup.gexf4j.core.data.AttributeList;
import it.uniroma1.dis.wsngroup.gexf4j.core.data.AttributeType;
import it.uniroma1.dis.wsngroup.gexf4j.core.impl.GexfImpl;
import it.uniroma1.dis.wsngroup.gexf4j.core.impl.StaxGraphWriter;
import it.uniroma1.dis.wsngroup.gexf4j.core.impl.data.AttributeListImpl;
import it.uniroma1.dis.wsngroup.gexf4j.core.impl.viz.PositionImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * newFile
 * ============================================================================
 * author : dzw
 * createDate:  2020/12/25 。
 * ============================================================================
 */
@Slf4j
@Controller
public class WikiController extends BaseController {

	@Autowired
	private TargetService targetService;
	@Autowired
	private TargetDescService targetDescService;
	@Autowired
	private TargetDescZanService targetDescZanService;
	@Autowired
	private TargetDescEvaluateService targetDescEvaluateService;

	/**
	 * 跳转搜索首页
	 */
	@GetMapping("search")
	public String search(@CurrentUser User user, Model model){
		List<Dict> dictList = dictService.findDictList(DictEnum.target_class);
		model.addAttribute("targetClassList", dictList);
		return "wiki/search";
	}

	/**
	 * 跳转搜索结果页
	 */
	@GetMapping("search_result/{key}")
	public String search_result(@PathVariable String key,  Model model, String p){
		int page = 1;
		if(NumberUtil.isNumber(p)){
			page = Convert.toInt(p);
			if(page < 1) page = 1;
		}
		Pageable pageable = PageRequest.of(page-1, 10, new Sort(Sort.Direction.DESC, "createTime"));

		List<Rule> rules = new ArrayList<>();
		rules.add(Rule.eq("state", BaseEnumManager.StateEnum.Enable));
		List<Rule> orRules = new ArrayList<>();
		orRules.add(Rule.like("name", key));
		orRules.add(Rule.like("targetNo", key));
		orRules.add(Rule.like("targetClassDict.name", key));
		orRules.add(Rule.like("targetLabel", key));
		Page<Target> pageContent = targetService.findAll(SpecificationFactory.wheres(SpecificationFactory.where(rules).and(SpecificationFactory.or(orRules))).build(), pageable);

		model.addAttribute("targetList", pageContent.getContent());
		model.addAttribute("key", key);
		model.addAttribute("page", JqGridReturn.build(null, pageContent));
		return "wiki/search_result";
	}

	/**
	 * 进入wiki详情页
	 */
	@GetMapping("wiki/{id}")
	public String wiki(@CurrentUser User user,@PathVariable String id, Model model){
		Target target = targetService.get(id);
		model.addAttribute("target", target);

		String userId = user!=null ? user.getId() : "";
		List<String> zanDescIds = targetDescZanService.findZanDescIds(id, userId);

		List<WikiDescVo> wikiDescVoList = new ArrayList<>();
		List<NameValueVo> nameValueVos = targetDescService.findTargetDescDictList(id);
		for(NameValueVo nameValueVo : nameValueVos){
			List<Rule> rules = new ArrayList<>();
			rules.add(Rule.eq("state", BaseEnumManager.StateEnum.Enable));
			rules.add(Rule.eq("processState", FlowEnum.ProcessState.Finished));
			rules.add(Rule.eq("target.id", id));
			rules.add(Rule.eq("targetDescDict.id", nameValueVo.getValue()));
			List<WikiDescVo.WikiTargetDescVo> wikiTargetDescVoList = new ArrayList<>();
			targetDescService.findAll(SpecificationFactory.where(rules)).forEach(desc -> wikiTargetDescVoList.add(new WikiDescVo.WikiTargetDescVo(desc, CollUtil.contains(zanDescIds, desc.getId()))));
			wikiDescVoList.add(new WikiDescVo(nameValueVo.getValue(), nameValueVo.getName(), wikiTargetDescVoList));
		}
		model.addAttribute("wikiDescList", wikiDescVoList);

		//查看次数+1
		targetService.addReadCount(target);

		//雷达图的五个评分标准indicator
		List<Dict> dictList = getDictList(DictEnum.target_evaluate_type);
		List<Map<String, Object>> evaluateTypeList = new ArrayList<>();
		dictList.forEach(dict -> {
			Map<String, Object> map = new LinkedHashMap<>();
			map.put("name", dict.getName());
			map.put("max", 5);
			map.put("min", -5);
			evaluateTypeList.add(map);
		});
		model.addAttribute("evaluateTypeList", JSONUtil.parseArray(evaluateTypeList));
		return "wiki/wiki";
	}

	/**
	 * 获取指标关系图xml
	 * , produces = "text/html;charset=utf-8"
	 */
	@RequestMapping(value = "relatedXml/{id}")
	@ResponseBody
	public String relatedXml(@PathVariable String id) throws IOException {
		Target target = targetService.get(id);
		if(target == null) return null;
		List<Target> targetList = targetService.findAllInIds(target.getRelatedTargetIds());
		String s = getGexfStr(target, targetList);
		request.setCharacterEncoding("utf-8");  //这里不设置编码会有乱码
		response.setContentType("text/html;charset=utf-8");
		response.setHeader("Cache-Control", "no-cache");
		PrintWriter out = response.getWriter();  //输出中文，这一句一定要放到response.setContentType("text/html;charset=utf-8"),  response.setHeader("Cache-Control", "no-cache")后面，否则中文返回到页面是乱码
		out.print(s);
		out.flush();
		out.close();

		return null;
	}

	private String getGexfStr(Target target, List<Target> childrenList){
		Gexf gexf = new GexfImpl();
		gexf.getMetadata().setLastModified(new Date()).setCreator("ZhengWei Dong").setDescription("关系图模型");
		gexf.setVisualization(true);

		Graph graph = gexf.getGraph();
		graph.setDefaultEdgeType(EdgeType.UNDIRECTED).setMode(Mode.STATIC);

		AttributeList attrList = new AttributeListImpl(AttributeClass.NODE);
		graph.getAttributeLists().add(attrList);

		Attribute modularity_class = attrList.createAttribute("modularity_class", AttributeType.INTEGER, "Modularity Class");

		// ====================================================================================
		List<String> hasList = new ArrayList<>();
		hasList.add(target.getId());

		float x1 = 10; float y1 = 10;
		Node n1 = graph.createNode(target.getId());
		n1.setLabel(target.getName())
				.setSize(100)
				.setPosition(new PositionImpl(x1, y1, 0))
				.getAttributeValues()
				.addValue(modularity_class, "0");

		for(int i=0; i<childrenList.size(); i++){
			int index = i+2;
			float[] xyVal2 = getPosition(x1, y1, 2, childrenList.size(), i);
			Target t2 = childrenList.get(i);
			if(CollUtil.contains(hasList, t2.getId())) continue;
			Node n2 = graph.createNode(t2.getId());
			hasList.add(t2.getId());
			n2.setLabel(t2.getName())
					.setSize(60)
					.setPosition(new PositionImpl(xyVal2[0], xyVal2[1], 0))
					.getAttributeValues()
					.addValue(modularity_class, Convert.toStr(index-1));
			n1.connectTo(StrUtil.format("{}_{}", target.getId(), t2.getId()), n2).setWeight(0.8f);

			List<Target> childrenList2 = targetService.findAllInIds(t2.getRelatedTargetIds());
			CollUtil.removeAny(childrenList2, target);
			for(int j=0; j<childrenList2.size(); j++){
				float[] xyVal3 = getPosition(xyVal2[0], xyVal2[1], 3, childrenList2.size(), j);
				Target t3 = childrenList2.get(j);
				if(CollUtil.contains(hasList, t3.getId())) continue;
				Node n3 = graph.createNode(t3.getId());
				hasList.add(t3.getId());
				n3.setLabel(t3.getName())
						.setSize(20)
						.setPosition(new PositionImpl(xyVal3[0], xyVal3[1], 0))
						.getAttributeValues()
						.addValue(modularity_class, Convert.toStr(index-1));
				n2.connectTo(StrUtil.format("{}_{}", t2.getId(), t3.getId()), n3).setWeight(0.8f);

				List<Target> childrenList3 = targetService.findAllInIds(t3.getRelatedTargetIds());
				childrenList3.forEach(c -> {
					if(c.getId().equals(target.getId())){
						n1.connectTo(StrUtil.format("{}_{}", target.getId(), t3.getId()), n3).setWeight(0.8f);
					}
				});

			}

		}

		StaxGraphWriter graphWriter = new StaxGraphWriter();
		File f = FileUtil.createTempFile("gexf", ".gexf", FileUtil.getTmpDir(), true);
		Writer out;
		try {
			out =  new FileWriter(f, false);
			graphWriter.writeToStream(gexf, out, "UTF-8");
		} catch (IOException e) {
			e.printStackTrace();
		}

		return FileUtil.readUtf8String(f);
	}

	private float[] getPosition(float px, float py, int level, int count, int index){
		float[] arr = new float[2];
		index = index +1;

		float julicha = level > 2 ? 1 : 10;
		if(index%2 != 0) julicha = 0 - julicha;

		float pxFirst = px + julicha;
		float pyFirst = py + julicha;

		float yuanJulicha = 2;
		if(count > 8) yuanJulicha = 4;
		if(count > 16) yuanJulicha = 6;
		if(index%2 != 0){
			yuanJulicha = 0 - yuanJulicha;
		}

		float pxIndex = pxFirst + (yuanJulicha*index)*2;
		float pyIndex = pyFirst + (yuanJulicha*index);
		if(index%2 != 0){
			pxIndex = pxIndex +7;
			pyIndex = pyIndex -7;
		}
		if(index%3 != 0){
			pxIndex = pxIndex -27;
			pyIndex = pyIndex +17;
		}
		if(index%4 != 0){
			pxIndex = pxIndex +18;
			pyIndex = pyIndex -9;
		}

		arr[0] = pxIndex;
		arr[1] = pyIndex;
		return arr;
	}

	/**
	 * 主页tree数据
	 */
	@RequestMapping("tree")
	@ResponseBody
	public Object tree(String id, String name, String level){
		List<WikiTreeVo> wikiTreeVoList = new ArrayList<>();

		// 第一层
		if(StrUtil.isBlank(level)){
			List<Dict> dictList = dictService.findDictList(DictEnum.target_class);
			dictList.forEach(dict -> wikiTreeVoList.add(new WikiTreeVo("true", dict.getId(), dict.getName())));
		}

		// 第二层
		if(StrUtil.equals("0", level)){
			wikiTreeVoList.add(new WikiTreeVo("true", StrUtil.format("{}_{}", id, "1"), "一级"));
			wikiTreeVoList.add(new WikiTreeVo("true", StrUtil.format("{}_{}", id, "2"), "二级"));
			wikiTreeVoList.add(new WikiTreeVo("true", StrUtil.format("{}_{}", id, "3"), "三级"));
		}

		//第三层
		if(StrUtil.equals("1", level) && StrUtil.containsAny(id, "_")){
			String dictId = StrUtil.split(id, "_")[0];
			String levelInteger = StrUtil.split(id, "_")[1];
			List<Rule> ruleList = ListUtil.toList(
					Rule.eq("state", BaseEnumManager.StateEnum.Enable),
					Rule.eq("targetClassDict.id", dictId),
					Rule.eq("level", Convert.toInt(levelInteger))
			);
			List<Target> targetList = targetService.findAll(SpecificationFactory.where(ruleList));
			targetList.forEach(target -> wikiTreeVoList.add(new WikiTreeVo("false", StrUtil.format("{}+{}", id, target.getId()), target.getName())));
		}

		return wikiTreeVoList;
	}

	/**
	 * 主页tree数据（一次性获取全部数据）
	 */
	@RequestMapping("treeAjax")
	@ResponseBody
	public Object treeAjax(){
		List<Map<String, String>> list = new ArrayList<>();

		List<Dict> dictList = dictService.findDictList(DictEnum.target_class);
		dictList.forEach(dict -> {
			Map<String, String> data = new LinkedHashMap<>();
			data.put("id", dict.getId());
			data.put("pId", "0");
			data.put("name", StrUtil.format("<span style='font-size: 20px; font-weight: bold; line-height: 35px'>{}</span>", dict.getName()));
			data.put("open", "true");
			list.add(data);

			data = new LinkedHashMap<>();
			data.put("id", dict.getId() + "_1");
			data.put("pId", dict.getId());
			data.put("name", StrUtil.format("<span style='font-size: 14px; line-height: 35px'>{}</span>", "一级指标"));
			data.put("open", "true");
			list.add(data);

			data = new LinkedHashMap<>();
			data.put("id", dict.getId() + "_2");
			data.put("pId", dict.getId());
			data.put("name", StrUtil.format("<span style='font-size: 14px; line-height: 35px'>{}</span>", "二级指标"));
			data.put("open", "true");
			list.add(data);

			data = new LinkedHashMap<>();
			data.put("id", dict.getId() + "_3");
			data.put("pId", dict.getId());
			data.put("name", StrUtil.format("<span style='font-size: 14px; line-height: 35px'>{}</span>", "三级指标"));
			data.put("open", "true");
			list.add(data);
		});

		List<Rule> rules = new ArrayList<>();
		rules.add(Rule.eq("state", BaseEnumManager.StateEnum.Enable));
		rules.add(Rule.eq("processState", FlowEnum.ProcessState.Finished));
		List<Target> targetList = targetService.findAll(SpecificationFactory.where(rules));
		targetList.forEach(target -> {
			Map<String, String> data = new LinkedHashMap<>();
			Dict targetClassDict = target.getTargetClassDict();
			if(targetClassDict != null){
				data.put("id", targetClassDict.getId() + "+" + target.getId());
				data.put("pId", targetClassDict.getId() + "_" + target.getLevel());
				data.put("name", StrUtil.format("<span style='font-size: 14px; line-height: 35px'>{}</span>", target.getName()));
				list.add(data);
			}
		});

		return list;
	}

	/**
	 * 新版树结构（类型也在树结构里，一次性全部查出来）
	 */
	@RequestMapping("treeAjax2")
	@ResponseBody
	public Object treeAjax2(){
		return targetService.getTree();
	}

	/**
	 * 新版树结构（根据类型一个一个查，树结构中不包含类型）
	 */
	@RequestMapping("treeAjax3")
	@ResponseBody
	public Object treeAjax3(String targetClassId){
		return targetService.getTreeByTargetClass(targetClassId);
	}

	/**
	 * 获取指标分类列表
	 */
	@RequestMapping("getTargetClassList")
	@ResponseBody
	public AjaxResult getTargetClassList() {
		List<String> dictList = new ArrayList<>();
		return AjaxResult.success("success", dictList);
	}

	/**
	 * 获取主页各种数量数据
	 */
	@RequestMapping("getAnyCount")
	@ResponseBody
	public AjaxResult getAnyCount(){
		Map<String, Object> data = new LinkedHashMap<>();

		List<Rule> rules_comm = new ArrayList<>();
		rules_comm.add(Rule.eq("state", BaseEnumManager.StateEnum.Enable));
		rules_comm.add(Rule.eq("processState", FlowEnum.ProcessState.Finished));

		//指标数
		Long zhibiaoshu = targetService.count(SpecificationFactory.where(rules_comm));
		data.put("zhibiaoshu", zhibiaoshu);

		//活跃指标数
		List<Rule> rules1 = new ArrayList<>(rules_comm);
		rules1.add(Rule.ge("readCount", 10));
		Long huoyuezhibiaoshu = targetService.count(SpecificationFactory.where(rules1));
		data.put("huoyuezhibiaoshu", huoyuezhibiaoshu);

		//指标描述
		Long zhibiaomiaoshu = targetDescService.count(SpecificationFactory.where(rules_comm));
		data.put("zhibiaomiaoshu", zhibiaomiaoshu);

		//相关指标标签
		data.put("zhibiaobiaoqian", targetService.getTargetLabelCount());

		//指标评论数
		data.put("zhibiaopinglunshu", targetDescEvaluateService.findEnableCount());

		return AjaxResult.success("获取成功", data);
	}

}
